//
//  QYAPIRequest.swift
//  QYNetworkSDK
//
//  Created by blazer on 2022/9/21.
//
// https://github.com/Lebron1992/learning-notes/blob/master/docs/alamofire5/02%20-%20Alamofire%205%20%E7%9A%84%E4%BD%BF%E7%94%A8%20-%20%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95.md#alamofire-5-%E7%9A%84%E4%BD%BF%E7%94%A8---%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95
// 缺少的功能:1. 取消请求 2.上传文件之类的带参数 3.断点续传 4.批量下载

import Foundation
import Alamofire

public enum BLNetworkError: Error {
    case hostUrlEmpty(String)
    case validateFaile(String)
}

public enum BLRequestSerializer: String {
    case none  // 二进制
    case json
}

public enum BLHTTPMethod: String {
    case GET
    case POST
}

public protocol BLAPIReqeust {
    associatedtype Response: BLDecodable
    associatedtype Parameter : Encodable
    
    var timeOut: TimeInterval { get }
    
    var host: String { get set } // default -- QYAPIRequestConfigure.shared.baseUrl
    // 必须赋值
    var path: String { get set} 
        
    // 有默认实现
    var method: BLHTTPMethod { get }  // default .GET
    var parameter: Parameter? { get } // default nil
    var headers: [String : String] { get } // default [:]
    var requestSerializer: BLRequestSerializer { get } // default .none
    var progressClosue: Request.ProgressHandler? {get}
}

public extension BLAPIReqeust {
    
    var timeOut: TimeInterval {
        return 10
    }
    
    var host: String {
        return BLAPIRequestConfigure.shared.baseUrl
    }
    
    var method: BLHTTPMethod {
        return .GET
    }
    
    var requestSerializer: BLRequestSerializer {
        return .none
    }
    
    var headers: [String:String] {
        return [:]
    }
    
    var parameter: Parameter? {
        return nil
    }
    
    var progressClosue: Request.ProgressHandler? {
        return nil
    }
}

public protocol BLDownloadRequest: BLAPIReqeust {
    func destinationPath(_ temporaryURL: URL, response: HTTPURLResponse) -> URL // defalut -- nil  存放在系统临时目录中。它最终会在将来的某个时候被系统删除
}

public extension BLDownloadRequest {
    func destinationPath(_ temporaryURL: URL, response: HTTPURLResponse) -> URL {
        return URL(string: NSTemporaryDirectory())!.appendingPathComponent(response.suggestedFilename ?? "")
    }
}

//public typealias Destination = (_ temporaryURL: URL,
//                                _ response: HTTPURLResponse) -> (destinationURL: URL, options: Options)

protocol BLClient {
    func send<T:BLAPIReqeust>(_ r: T, handler: @escaping(Swift.Result<T.Response?, Error>) -> Void)
    func upload<T:BLAPIReqeust>(_ data: Data, r: T, handler: @escaping(Swift.Result<T.Response?, Error>) -> Void)
    func upload<T:BLAPIReqeust>(_ filePath: URL, r: T, handler: @escaping(Swift.Result<T.Response?, Error>) -> Void)
}

public protocol BLDownloadClient {
    var host: String { get }  // default -- QYAPIRequestConfigure.shared.baseUrl
    func downLoad<T:BLDownloadRequest>(_ path: URL, r: T, handler: @escaping(Swift.Result<URL?, Error>) -> Void);
}

public extension BLDownloadClient {
    var host: String {
        return BLAPIRequestConfigure.shared.baseUrl
    }
}

public protocol BLDecodable {
    static func parse(data: Data) -> Self?
    static func validate(request: URLRequest?, data: Data?) -> Swift.Result<Void, Error> //default .success(())
}

public extension BLDecodable {
    static func parse(data: Data) -> Self? {
        return nil
    }
    
    static func validate(request: URLRequest?, data: Data?) -> Swift.Result<Void, Error> {
        return .success(())
    }
}

public class BLAPIRequestConfigure {
    public static let shared = BLAPIRequestConfigure()
    public var baseUrl: String = ""
}

public class BLAPIRequestClient: NSObject, BLClient {
    public static let shared = BLAPIRequestClient()
    
    private var customSession: Session? = nil
    
    public override init(){
        super.init()
    }
    
    // 忽略SSL证书的问题
    public func customSend<T>(_ r: T, handler: @escaping (Swift.Result<T.Response?, Error>) -> Void) where T : BLAPIReqeust {
        
        guard !r.host.isEmpty else {
            handler(.failure(BLNetworkError.hostUrlEmpty("host url path is empty")))
            return
        }
        
        let method = HTTPMethod.init(rawValue: r.method.rawValue)
        let parameterEncoder: ParameterEncoder = r.requestSerializer == .json ? JSONParameterEncoder.default : URLEncodedFormParameterEncoder.default
        var headers: [HTTPHeader] = []
        if !r.headers.isEmpty {
            for e in r.headers {
                headers.append(HTTPHeader.init(name: e.key, value: e.value))
            }
        }
        debugPrint("QYAPIRequest:\(r.host.appending(r.path))?\(String(describing: r.parameter))")
        
        let serverTrusManager = ServerTrustManager(evaluators: ["ipc.daguiot.com": DisabledTrustEvaluator()])
        
        customSession = Session(serverTrustManager: serverTrusManager)
        
        let request = customSession!.request(r.host.appending(r.path),
                                      method: method,
                                      parameters: r.parameter,
                                      encoder: parameterEncoder,
                                      headers: headers.isEmpty ? nil : HTTPHeaders.init(headers),
                                      interceptor: nil) { urlRequest in
            urlRequest.timeoutInterval = r.timeOut
        }
        
        if r.progressClosue != nil {
            request.downloadProgress(closure: r.progressClosue!)
        }
        
        request.validate { requst, _, data in
            return T.Response.validate(request: request.request, data: data)
        }.response { res in
            switch res.result {
            case .success(let d):
                if let tmp = d, let r = T.Response.parse(data: tmp) {
                    debugPrint("QYAPIRequest-response:\(String.init(data: tmp, encoding: .utf8) ?? "")")
                    handler(.success(r))
                } else {
                    debugPrint("QYAPIRequest-response:nil")
                    handler(.success(nil))
                }
                break
            case .failure(let err):
                debugPrint("QYAPIRequest-response-error: \(res.request?.url?.absoluteString ?? "")\(err)")
                handler(.failure(err))
            }
        }
    }
    
    public func send<T>(_ r: T, handler: @escaping (Swift.Result<T.Response?, Error>) -> Void) where T : BLAPIReqeust {
        guard !r.host.isEmpty else {
            handler(.failure(BLNetworkError.hostUrlEmpty("host url path is empty")))
            return
        }
        
        let method = HTTPMethod.init(rawValue: r.method.rawValue)
        let parameterEncoder: ParameterEncoder = r.requestSerializer == .json ? JSONParameterEncoder.default : URLEncodedFormParameterEncoder.default
        var headers: [HTTPHeader] = []
        if !r.headers.isEmpty {
            for e in r.headers {
                headers.append(HTTPHeader.init(name: e.key, value: e.value))
            }
        }
        debugPrint("QYAPIRequest:\(r.host.appending(r.path))?\(String(describing: r.parameter))")
        let request = AF.request(r.host.appending(r.path),
                                 method: method,
                                 parameters: r.parameter,
                                 encoder: parameterEncoder,
                                 headers: headers.isEmpty ? nil : HTTPHeaders.init(headers),
                                 interceptor: nil) { urlRequest in
            urlRequest.timeoutInterval = r.timeOut
//            urlRequest.allowsCellularAccess = false
//            urlRequest.cachePolicy = .reloadIgnoringLocalCacheData
//            urlRequest.httpShouldUsePipelining = true
//            urlRequest.httpShouldHandleCookies = false
        }// requestModifier 更改URLRequest 给URLRequest添加一些改变 比如设置超时时间 是否允许访问蜂窝网络
       
        if r.progressClosue != nil {
            request.downloadProgress(closure: r.progressClosue!)
        }
        
        request.validate { requst, _, data in
            return T.Response.validate(request: request.request, data: data)
        }.response { res in
            switch res.result {
            case .success(let d):
                if let tmp = d, let r = T.Response.parse(data: tmp) {
                    debugPrint("QYAPIRequest-response:\(String.init(data: tmp, encoding: .utf8) ?? "")")
                    handler(.success(r))
                } else {
                    debugPrint("QYAPIRequest-response:nil")
                    handler(.success(nil))
                }
                break
            case .failure(let err):
                debugPrint("QYAPIRequest-response-error: \(res.request?.url?.absoluteString ?? "")\(err)")
                handler(.failure(err))
            }
        }
    }
    
    public func upload<T:BLAPIReqeust>(_ data: Data, r: T, handler: @escaping(Swift.Result<T.Response?, Error>) -> Void) {
        guard !r.host.isEmpty else {
            handler(.failure(BLNetworkError.hostUrlEmpty("host url path is empty")))
            return
        }
        let method = HTTPMethod.init(rawValue: r.method.rawValue)
        var headers: [HTTPHeader] = []
        if !r.headers.isEmpty {
            for e in r.headers {
                headers.append(HTTPHeader.init(name: e.key, value: e.value))
            }
        }
        if r.parameter != nil {
            
        } else {
            let request = AF.upload(data,
                                    to: r.host.appending(r.path),
                                    method: method,
                                    headers: headers.isEmpty ? nil : HTTPHeaders.init(headers),
                                    interceptor: nil,
                                    fileManager: .default,
                                    requestModifier: nil)
            request.validate { requst, _, data in
                return T.Response.validate(request: request.request, data: data)
            }
            
            request.response { res in
                switch res.result {
                case .success(let data):
                    if let d = data, let r = T.Response.parse(data: d) {
                        handler(.success(r))
                    } else {
                        handler(.success(nil))
                    }
                    break
                case .failure(let err):
                    handler(.failure(err))
                    break
                }
                
            }
        }
    }
    
    func upload<T:BLAPIReqeust>(_ filePath: URL, r: T, handler: @escaping(Swift.Result<T.Response?, Error>) -> Void) {
        guard !r.host.isEmpty else {
            handler(.failure(BLNetworkError.hostUrlEmpty("host url path is empty")))
            return
        }
        let method = HTTPMethod.init(rawValue: r.method.rawValue)
        var headers: [HTTPHeader] = []
        if !r.headers.isEmpty {
            for e in r.headers {
                headers.append(HTTPHeader.init(name: e.key, value: e.value))
            }
        }
        if r.parameter != nil {
            
        } else {
            let request = AF.upload(filePath,
                                    to: r.host.appending(r.path),
                                    method: method,
                                    headers: headers.isEmpty ? nil : HTTPHeaders.init(headers),
                                    interceptor: nil,
                                    fileManager: .default,
                                    requestModifier: nil)
            
            request.validate { requst, _, data in
                return T.Response.validate(request: request.request, data: data)
            }
            
            request.response { res in
                switch res.result {
                case .success(let data):
                    if let d = data, let r = T.Response.parse(data: d) {
                        handler(.success(r))
                    } else {
                        handler(.success(nil))
                    }
                    break
                case .failure(let err):
                    handler(.failure(err))
                    break
                }
            }
        }
    }
}

public struct BLDownLoadRequestClient: BLDownloadClient {
    public func downLoad<T:BLDownloadRequest>(_ path: URL, r: T, handler: @escaping(Swift.Result<URL?, Error>) -> Void) {
        guard !r.host.isEmpty else {
            handler(.failure(BLNetworkError.hostUrlEmpty("host url path is empty")))
            return
        }
        let method = HTTPMethod.init(rawValue: r.method.rawValue)
        let parameterEncoder: ParameterEncoder = r.requestSerializer == .json ? JSONParameterEncoder.default : URLEncodedFormParameterEncoder.default
        var headers: [HTTPHeader] = []
        if !r.headers.isEmpty {
            for e in r.headers {
                headers.append(HTTPHeader.init(name: e.key, value: e.value))
            }
        }
        // 断点续传 保存完好了数据，下次直接调这个方法 AF.download(resumingWith: <#T##Data#>, interceptor: <#T##RequestInterceptor?#>, to: <#T##DownloadRequest.Destination?##DownloadRequest.Destination?##(_ temporaryURL: URL, _ response: HTTPURLResponse) -> (destinationURL: URL, options: DownloadRequest.Options)#>)
        AF.download(host.appending(r.path),
                    method: method,
                    parameters: r.parameter,
                    encoder: parameterEncoder,
                    headers: headers.isEmpty ? nil : HTTPHeaders.init(headers),
                    interceptor: nil,
                    requestModifier: nil) { temporaryURL, response in
            return (r.destinationPath(temporaryURL, response: response), .removePreviousFile) // .removePreviousFile 从目标URL中删除以前的文件 .createIntermediateDirctories 则为目标URL创建中间目录
        }.response { res in
            switch res.result {
            case .success(let url):
                handler(.success(url))
            case .failure(let err):
                handler(.failure(err))
            }
        }
    }
}

